#include "BASE64Codec.h"
#include <iostream>
using namespace tinkle;

BASE64Codec* BASE64Codec::_instance = NULL;

const BASE64Codec* BASE64Codec::Codec() {
	if (!_instance) {
		_instance = new BASE64Codec();
	}
	return _instance;
}


std::string BASE64Codec::Encode(const std::string &str) const {
	static const char *alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	unsigned char mask = 0x3f;

	if ( str.length() == 0 ) return "";
	
	std::string ret( ( (str.length()-1)/3+1 ) * 4, '=');
	int offset = 0; unsigned char rest = 0;
	int len = str.length();
	int p = 0;

	for (int i = 0; i < len; i++) {
		offset = offset+2;
		ret[p++] = alpha[rest + (str[i] >> offset) & mask];
		rest = (((unsigned char)str[i]) << (6-offset))&mask;
		if ( 6 == offset) {
			ret[p++] = alpha[rest];
			rest = 0; offset = 0;
		}
	} 
	if (offset != 0) ret[p++] = alpha[rest];
	return ret;
}

std::string BASE64Codec::Decode(const std::string &str) const {
	#define BASE64_TO_BYTE(c) ( (c >= 'a') ? (26+c-'a') : ( ( c >= 'A' ) ? ( c - 'A') : (  (c >= '0') ? (c-'0'+52) : ( (c == '/') ? 63 : (c == '+' ? 62:64)))))
	if ( str.length() == 0 ) return "";

	int len = str.length();
	int p = 0, offset = 0;
	unsigned char rest = 0;
	std::string ret(len/4*3, ' ');
	for (int i = 0; i < len && str[i] != '='; i++) {
		//rest = BASE64_TO_BYTE(str[0]);
		if (offset != 0) {
			ret[p++] = (rest << offset)+(BASE64_TO_BYTE(str[i]) >> (6-offset));
		}
		rest = BASE64_TO_BYTE(str[i]) & (0x3f >> offset);				
		offset = (offset+2) % 8;
	}
	ret.erase(p);
	return ret;
}